﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sinacor.OMM.Util;
using Sinacor.OMM.ConnectionManager.ErrorHandlers;
using System.Messaging;

namespace Sinacor.OMM.ConnectionManager.MSMQ
{
    /// <summary>
    /// Creates and manages a connection with a message queue.
    /// </summary>
    public class MsmqSender : IDisposable
    {
        #region Constants
        private const string TRACE_FOLDER_NAME = "ConnectionManager";
        #endregion

        #region Attributes
        private PSE.Framework.Messaging.MsmqSender _sender;
        private string _traceFileName;
        private string _traceFileNameHealthStatus;
        private TraceWriter _traceWriter;
        private TraceWriter _traceWriterHealthStatus;
        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the last error code that has occurred.
        /// </summary>
        public int LastErrorCode 
        {
            get { return _sender.LastErrorCode; }
        }

        /// <summary>
        /// Gets or sets the last error message that has occurred.
        /// </summary>
        public string LastErrorMessage
        {
            get { return _sender.LastErrorMessage; }
        }

        /// <summary>
        /// Gets or sets the queue's path.
        /// </summary>
        public string Path
        {
            get { return _sender.Path; }
        }

        /// <summary>
        /// Gets the size in bytes of the queue.
        /// </summary>
        public long QueueSizeBytes
        {
            get { return _sender.QueueSizeBytes; }
        }

        /// <summary>
        /// Gets the number of messages in the queue.
        /// </summary>
        public int QueuedMessages
        {
            get { return _sender.QueuedMessages; }
        }

        /// <summary>
        /// Gets or sets the limit of the queue in bytes. 
        /// </summary>
        public long QueuedBytesLimit
        {
            get { return _sender.QueuedBytesLimit; }
            set { _sender.QueuedBytesLimit = value; }
        }

        /// <summary>
        /// Gets or sets the limit of the queue in message number. 
        /// </summary>
        public int QueuedMessageLimit
        {
            get { return _sender.QueuedMessageLimit; }
            set { _sender.QueuedMessageLimit = value; }
        }

        /// <summary>
        /// Gets a value indicating whether the queue exceeds either the limit in bytes or the limit in message number.
        /// </summary>
        public bool QueueExceedsLimit
        {
            get { return _sender.QueueExceedsLimit; }            
        }

        /// <summary>
        /// Gets the number of messages sent per second by the MsmqSender object.
        /// </summary>
        public int MessagesSentPerSecond
        {
            get { return _sender.MessagesSentPerSecond; }
        }      

        /// <summary>
        /// Gets a value indicating whether the MsmqSender is connected to a queue.
        /// </summary>
        public bool IsConnected
        {
            get { return _sender.IsConnected; }
        }        

        /// <summary>
        /// Gets the machine's name of the queue's path.
        /// </summary>
        public string MachineName
        {
            get { return _sender.MachineName; }
        }

        /// <summary>
        /// Gets the queue's name of the queue's path.
        /// </summary>
        public string QueueName
        {
            get { return _sender.QueueName; }
        }

        /// <summary>
        /// Gets the format's name of the queue's path.
        /// </summary>
        public string FormatName
        {
            get { return _sender.FormatName; }
        }

        #endregion

        #region Delegates
        /// <summary>
        /// <para>Represents the method that will handle the Sinacor.OMM.ConnectionManager.MSMQ.MsmqSender.OnHealthStatusChanged event</para>
        /// of a Sinacor.OMM.ConnectionManager.MSMQ.MsmqSender object.
        /// </summary>
        /// <param name="msmqSender"></param>
        public delegate void HealthStatusChanged(MsmqSender msmqSender);
        #endregion

        #region Events
        /// <summary>
        /// Occurs when the PSE.Framework.Messaging.MsmqSender object changed its state.
        /// </summary>
        public event HealthStatusChanged OnHealthStatusChanged;
        #endregion

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the PSE.Framework.Messaging.MsmqSender class.
        /// </summary>
        /// <param name="path">The path of the message queue.</param>        
        /// <param name="queueMessageLimit">The maximum number of messages that the queue can enqueue.</param>
        /// <param name="queueBytesLimit">The maximum size in bytes that the queue can store.</param>
        public MsmqSender(string path, int queueMessageLimit, long queueBytesLimit)
        {
            _traceFileName = "MsmqSender " + path.Substring(path.LastIndexOf("\\") + 1);
            _traceFileNameHealthStatus = "MsmqSender HealthStatus " + path.Substring(path.LastIndexOf("\\") + 1);

            _traceWriter = Tracer.GetInstance(TRACE_FOLDER_NAME, _traceFileName);
            _traceWriterHealthStatus = Tracer.GetInstance(TRACE_FOLDER_NAME, _traceFileNameHealthStatus);

            _sender = new PSE.Framework.Messaging.MsmqSender(path, queueMessageLimit, queueBytesLimit);
            _sender.Connected += new EventHandler(_sender_Connected);
            _sender.Disconnected += new EventHandler(_sender_Disconnected);
            _sender.Error += new PSE.Framework.Messaging.ErrorEventHandler(_sender_Error);            
            _sender.OnHealthStatusChanged += new PSE.Framework.Messaging.MsmqSender.HealthStatusChanged(_sender_OnHealthStatusChanged);

            _traceWriter.Trace("Queue Path.........: " + _sender.Path);
            _traceWriter.Trace("Limite Mensagens...: " + _sender.QueuedMessageLimit + " mensagens");
            _traceWriter.Trace("Limite Tamanho.....: " + _sender.QueuedBytesLimit + " bytes");
            _traceWriter.Trace("The MsmqSender iniciou com sucesso.");
        }
        
        #endregion        

        #region Methods

        #region _sender object
        private void _sender_OnHealthStatusChanged(PSE.Framework.Messaging.MsmqSender msmqSender)
        {
            _traceWriterHealthStatus.Trace("Tamanho da fila....: [" + msmqSender.QueueSizeBytes + "] bytes.", TraceType.Optional);
            _traceWriterHealthStatus.Trace("Mensagens na fila..: [" + msmqSender.QueuedMessages + "].", TraceType.Optional);

            if (OnHealthStatusChanged != null)
                OnHealthStatusChanged(this);
        }        

        private void _sender_Error(object sender, PSE.Framework.Messaging.ErrorEventArgs e)
        {
            ErrorHandler errorHandler = new ErrorHandler();
            errorHandler.Exception = e.Exception;
            errorHandler.Msmq = (PSE.Framework.Messaging.MsmqSender)sender;

            switch (e.ErrorCode)
            {
                case PSE.Framework.Messaging.MsmqSender.ERROR_CODE_CONNECTION_FAILURE:
                    errorHandler.Handler = new ConnectionFailure();
                    break;
                case PSE.Framework.Messaging.MsmqSender.ERROR_CODE_CREATING_MSMQ_MANAGEMENT:
                    errorHandler.Handler = new CreatingMsmqManagement();
                    break;
                case PSE.Framework.Messaging.MsmqSender.ERROR_CODE_UNEXPECTED_FAILURE_ON_CONNECT:
                    errorHandler.Handler = new UnexpectedFailureOnConnect();
                    break;
                case PSE.Framework.Messaging.MsmqSender.ERROR_CODE_MSMQ_CANT_SEND:
                    errorHandler.Handler = new MsmqCanNotSend();
                    break;
                case PSE.Framework.Messaging.MsmqSender.ERROR_CODE_MSMQ_FAILURE_ON_PURGE_QUEUE:
                    errorHandler.Handler = new FailureOnPurgeQueue();
                    break;
                case PSE.Framework.Messaging.MsmqSender.ERROR_CODE_QUEUE_DOESNT_EXIST:
                    errorHandler.Handler = new QueueDoesNotExist();
                    break;
                case PSE.Framework.Messaging.MsmqSender.ERROR_CODE_QUEUE_LIMIT_REACH:
                    errorHandler.Handler = new QueueLimitReached();
                    break;
                case PSE.Framework.Messaging.MsmqSender.ERROR_CODE_READING_QUEUE_MESSAGE_QUANTITY:
                    errorHandler.Handler = new ReadingQueueMessageQuantity();
                    break;
                case PSE.Framework.Messaging.MsmqSender.ERROR_CODE_READING_QUEUE_SIZE:
                    errorHandler.Handler = new ReadingQueueSize();
                    break;
                case PSE.Framework.Messaging.MsmqSender.ERROR_CODE_UNEXPECTED_FAILURE_ON_CHECK_HEALTH_STATUS:
                    errorHandler.Handler = new UnexpectedFailureOnCheckHealthStatus();
                    break;
                case PSE.Framework.Messaging.MsmqSender.ERROR_CODE_UNEXPECTED_FAILURE_ON_THREAD_RUNNING:
                    errorHandler.Handler = new UnexpectedFailureOnThreadRunning();
                    break;
                case PSE.Framework.Messaging.MsmqSender.ERROR_CODE_UNEXPECTED_FAILURE_ON_PURGE_QUEUE:
                    errorHandler.Handler = new UnexpectedFailureOnPurgeQueue();
                    break;
                default:
                    errorHandler.Handler = new GenericErrorHandler();
                    break;
            }

            _traceWriter.Trace(errorHandler.HandleError());            
        }

        private void _sender_Disconnected(object sender, EventArgs e)
        {
            _traceWriter.Trace("O MsmqSender desconectou.");
        }

        private void _sender_Connected(object sender, EventArgs e)
        {
            _traceWriter.Trace("O MsmqSender conectou.");
        }        
        #endregion        

        /// <summary>
        /// Sends a message to the queue.
        /// </summary>
        /// <param name="message">The message to be sent.</param>
        /// <returns>Returns a value indicating whether the sending was successful.</returns>
        public bool Send(Message message)
        {
            return _sender.Send(message);
        }

        /// <summary>
        /// Deletes all the messages contained in the queue.
        /// </summary>
        /// <returns>Returns a value indicating whether the deletion was successful.</returns>
        public bool Purge()
        {
            return _sender.Purge();
        }

        #endregion        

        #region IDisposable Members

        /// <summary>
        /// Releases all resources.
        /// </summary>
        public void Dispose()
        {
            Tracer.RemoveInstance(TRACE_FOLDER_NAME, _traceFileName);
            Tracer.RemoveInstance(TRACE_FOLDER_NAME, _traceFileNameHealthStatus);

            if (_sender != null)
                _sender.Dispose();
            _sender = null;
        }

        #endregion
    }
}
